/*
 * Copyright (c) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include <string>
#include <stdio.h>
#include <string.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <math.h>
#include <sys/select.h>
#include <errno.h>
#include <pthread.h>
#include <securec.h>
#include <deque>
#include <time.h>
#include <pthread.h>

#include "hilog_wrapper.h"
#include "napi/native_api.h"
#include "napi/native_node_api.h"
#include "securec.h"

using namespace std;

#define MAX_CONNECT 10 // how many pending connections queue will hold
#define SOCKET_BUF_SIZE 128
#define IP_BUF_MAX 64
#define EXIT_ERR (-1)
#define SOCKET_OK (0)
#define SOCKET_ERR (-1)
#define DEFAULT_PORT 5000

#define EXEC_SUCC (0)
#define EXEC_FAILED (-1)

static int g_threadFlag = 1;
char recvClientMsg[SOCKET_BUF_SIZE] = {};

typedef struct _stContrlParam {
    unsigned int service_id; // 0:not connected
    unsigned int enable;     // socket connect status
    unsigned int countTick;  // Number of times that no heartbeat response is received
} stContrlParam;

enum SOCKET_CONCTRL {
    SOCKET_DISABLE = 0,
    SOCKET_ENABLE
};

stContrlParam g_contrl[MAX_CONNECT] = {};
int g_connAmount = 0; // current connection count

enum ResponseState {
    /**
     * 控制命令响应状态值.
     */
    SUCCESS = 200,
    ERROR = 500
};

// 响应值
class tcpserverapi {
public:
    /**
     * 响应状态
     */
    ResponseState state;

    /**
     * 设备状态
     */
    std::string result;

    /**
     * 响应时间
     */
    std::string time;
};

struct CommandStrData {
    napi_async_work asyncWork = nullptr;
    napi_deferred deferred = nullptr;
    napi_ref callback = nullptr;
    tcpserverapi result;
    int clientNum = 0;
    char robotDogCommand[32] = {};
    char recvMessage[SOCKET_BUF_SIZE] = {};
};

// ======================================tcpServerApi===============================================

/**
 * @brief 向指定的设备发送控制信息
 *
 * @param client accept设备的文件符
 * @param valueStr 有待传送的字符串
 * @return ssize_t 返回处理结果
 */
static ssize_t send_msg_to_robotDog(string valueStr)
{
    for (int i = 0; i < g_connAmount; i++) {
        char sendBuf[SOCKET_BUF_SIZE] = "";
        bzero(sendBuf, sizeof(sendBuf));
        valueStr.copy(sendBuf, valueStr.length(), 0);

        ssize_t sendNum = write(g_contrl[i].service_id, sendBuf, strlen(sendBuf));
        return sendNum;
    }
    return -1;
}

// 业务逻辑处理函数，由worker线程池调度执行。
static void sendRobotDogCommandExecuteCB(napi_env env, void *data)
{
    HILOG_INFO("=====%{public}s,called", __func__);
    CommandStrData *commandStrData = (CommandStrData *)data;

    // 发送命令
    string valueStr = commandStrData->robotDogCommand;

    ssize_t sendNum = send_msg_to_robotDog(valueStr);
    if (sendNum != -1) {
        commandStrData->result.state = SUCCESS;
        commandStrData->result.result = "SUCCESS";
    } else {
        commandStrData->result.state = ERROR;
        commandStrData->result.result = "NET ERROR";
    }
}

static void sendRobotDogCommandPromiseCompleteCB(napi_env env, napi_status status, void *data)
{
    CommandStrData *commandStrData = (CommandStrData *)data;

    napi_value result = nullptr;
    napi_value resState = nullptr;
    napi_value resResult = nullptr;
    napi_value resTime = nullptr;

    char tempChar[256] = {};
    napi_create_int32(env, (int32_t)commandStrData->result.state, &resState);
    strcpy_s(tempChar, sizeof(tempChar), commandStrData->result.result.c_str());
    napi_create_string_utf8(env, tempChar, strlen(tempChar), &resResult);

    bzero(tempChar, strlen(tempChar));
    strcpy_s(tempChar, sizeof(tempChar), commandStrData->result.time.c_str());
    napi_create_string_utf8(env, tempChar, strlen(tempChar), &resTime);

    napi_create_object(env, &result);
    napi_set_named_property(env, result, "state", resState);
    napi_set_named_property(env, result, "result", resResult);
    napi_set_named_property(env, result, "time", resTime);

    napi_resolve_deferred(env, commandStrData->deferred, result);

    // 删除napi_ref对象
    if (commandStrData->callback != nullptr) {
        napi_delete_reference(env, commandStrData->callback);
    }

    // 删除异步工作项
    napi_delete_async_work(env, commandStrData->asyncWork);
    delete commandStrData;
}

// 业务逻辑处理函数，由worker线程池调度执行。
static void recvCompassMsgExecuteCB(napi_env env, void *data)
{
    CommandStrData *commandStrData = (CommandStrData *)data;
    commandStrData->result.state = SUCCESS;
    commandStrData->result.result = "SUCCESS";
    commandStrData->result.time = "1s";
    // 连接到服务器的设备数量传递到应用层,如果为0，说明客户端没有客户端连接
    commandStrData->clientNum = g_connAmount;
    if (g_connAmount == 0) {
        bzero(recvClientMsg, sizeof(recvClientMsg));
    }
    // 接送到数据传递到应用层
    strcpy_s(commandStrData->recvMessage, sizeof(commandStrData->recvMessage), recvClientMsg);
}

static void recvCompassMsgPromiseCompleteCB(napi_env env, napi_status status, void *data)
{
    CommandStrData *commandStrData = (CommandStrData *)data;

    napi_value result = nullptr;
    napi_value resState = nullptr;
    napi_value resResult = nullptr;
    napi_value resTime = nullptr;
    napi_value resMessage = nullptr;
    napi_value resClientNumber = nullptr;

    char tempChar[256] = {};
    napi_create_int32(env, (int32_t)commandStrData->result.state, &resState);

    strcpy_s(tempChar, sizeof(tempChar), commandStrData->result.result.c_str());
    napi_create_string_utf8(env, tempChar, strlen(tempChar), &resResult);

    bzero(tempChar, strlen(tempChar));
    strcpy_s(tempChar, sizeof(tempChar), commandStrData->result.time.c_str());
    napi_create_string_utf8(env, tempChar, strlen(tempChar), &resTime);

    bzero(tempChar, strlen(tempChar));
    strcpy_s(tempChar, sizeof(tempChar), commandStrData->recvMessage);
    napi_create_string_utf8(env, tempChar, strlen(tempChar), &resMessage);

    napi_create_int32(env, (int32_t)commandStrData->clientNum, &resClientNumber);

    napi_create_object(env, &result);
    napi_set_named_property(env, result, "state", resState);
    napi_set_named_property(env, result, "result", resResult);
    napi_set_named_property(env, result, "time", resTime);
    napi_set_named_property(env, result, "message", resMessage);
    napi_set_named_property(env, result, "clientNumber", resClientNumber);

    napi_resolve_deferred(env, commandStrData->deferred, result);

    // 删除napi_ref对象
    if (commandStrData->callback != nullptr) {
        napi_delete_reference(env, commandStrData->callback);
    }

    // 删除异步工作项
    napi_delete_async_work(env, commandStrData->asyncWork);
    delete commandStrData;
}

static int SetConnectParam(int new_fd, int *maxsock)
{
    if (++g_connAmount == MAX_CONNECT) {
        g_connAmount--;
        HILOG_INFO("=====max connections arrived!\n");
        return -1;
    }
    for (int i = 0; i < MAX_CONNECT; i++) {
        if (g_contrl[i].enable == SOCKET_DISABLE) {
            g_contrl[i].enable = SOCKET_ENABLE;
            g_contrl[i].service_id = new_fd;
            break;
        }
    }

    if (new_fd > *maxsock) {
        *maxsock = new_fd;
    }

    return 0;
}

static void SocketRelease(int sock_fd)
{
    for (int i = 0; i < MAX_CONNECT; i++) {
        if (g_contrl[i].service_id != 0) {
            close(g_contrl[i].service_id);
            g_contrl[i].service_id = 0;
            g_contrl[i].enable = SOCKET_DISABLE;
        }
    }
    if (sock_fd) {
        close(sock_fd);
    }
}

static int ReadHandle(stContrlParam *ctrl, fd_set *fdSet)
{
    char recvbuf[SOCKET_BUF_SIZE] = {0};
    memset(recvbuf, '\0', sizeof(recvbuf));
    int recvbytes = read(ctrl->service_id, recvbuf, sizeof(recvbuf) - 1);
    if (recvbytes <= 0) {
        HILOG_INFO("=====close client=====%{public}d===\n", ctrl->service_id);
        close(ctrl->service_id);
        FD_CLR(ctrl->service_id, fdSet);
        ctrl->service_id = 0;
        ctrl->enable = SOCKET_DISABLE;
        g_connAmount--;
    } else {
        HILOG_INFO("=====server recv recvbytes=====%{public}d==%{public}s=\n", recvbytes, recvbuf);
        bzero(recvClientMsg, sizeof(recvClientMsg));
        strcpy_s(recvClientMsg, sizeof(recvClientMsg), recvbuf);
    }
    return 0;
}

static void HeartTick(fd_set *fdset)
{
    for (int i = 0; i < MAX_CONNECT; i++) {
        if (g_contrl[i].service_id != 0) {
            char bufr[SOCKET_BUF_SIZE] = {0};
            memset(bufr, '\0', sizeof(bufr));
            write(g_contrl[i].service_id, "{LIVE}", strlen("{LIVE}"));
            int ret = read(g_contrl[i].service_id, bufr, sizeof(bufr));
            HILOG_INFO("=====heart tick recv=====%{public}s==%{public}d=\n", bufr, ret);
            if (ret <= 0) {
                close(g_contrl[i].service_id);
                FD_CLR(g_contrl[i].service_id, fdset);
                g_contrl[i].enable = SOCKET_DISABLE;
                g_contrl[i].service_id = 0;
                g_contrl[i].countTick = 0;
                g_connAmount--;
                HILOG_INFO("=====close client=====%{public}d===\n", i);
            }
        }
    }
}

static int InitServer(int *sockfd)
{
    memset_s(g_contrl, sizeof(stContrlParam), 0, sizeof(stContrlParam) * MAX_CONNECT);
    g_connAmount = 0;
    int sock_fd = 0;
    int yes = 1;
    struct sockaddr_in server_addr;
    if ((sock_fd = socket(AF_INET, SOCK_STREAM, 0)) == SOCKET_ERR) {
        HILOG_INFO("===socket error=====\n");
        return -1;
    }

    if (setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == SOCKET_ERR) {
        HILOG_INFO("=====setsockopt error=====\n");
        close(sock_fd);
        return -1;
    }

    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(DEFAULT_PORT);
    server_addr.sin_addr.s_addr = INADDR_ANY;
    memset_s(server_addr.sin_zero, sizeof(server_addr.sin_zero), '\0', sizeof(server_addr.sin_zero));

    if (::bind(sock_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == SOCKET_ERR) {
        HILOG_INFO("=====bind error=====\n");
        close(sock_fd);
        return -1;
    }

    if (listen(sock_fd, MAX_CONNECT) == SOCKET_ERR) {
        HILOG_INFO("======listen error=====\n");
        close(sock_fd);
        return -1;
    }

    HILOG_INFO("======listen port==%{public}d===\n", DEFAULT_PORT);

    *sockfd = sock_fd;

    return 0;
}

static void *SocketServiceCreate(void *arg)
{
    int maxsock;
    int sock_fd = 0;
    struct sockaddr_in client_addr;
    socklen_t sin_size;
    fd_set fdset;
    struct timeval tv;

    if (InitServer(&sock_fd) < 0) {
        HILOG_INFO("=====InitServer failed=====\n");
        return nullptr;
    }

    sin_size = sizeof(client_addr);
    maxsock = sock_fd;
    while (g_threadFlag) {
        int ret, new_fd, i;
        FD_ZERO(&fdset);
        FD_SET(sock_fd, &fdset);
        tv.tv_sec = 10; // 10 second
        tv.tv_usec = 0;
        for (i = 0; i < MAX_CONNECT; i++) {
            if (g_contrl[i].service_id != 0) {
                FD_SET(g_contrl[i].service_id, &fdset);
            }
        }

        ret = select(maxsock + 1, &fdset, nullptr, nullptr, &tv);
        HILOG_INFO("=====select error===\n");
        if (ret < SOCKET_OK) {
            HILOG_INFO("=====select error=====\n");
            break;
        } else if (ret == SOCKET_OK) {
            HeartTick(&fdset);
            continue;
        }

        for (i = 0; i < MAX_CONNECT; i++) {
            if (FD_ISSET(g_contrl[i].service_id, &fdset)) {
                ReadHandle(&g_contrl[i], &fdset);
            }
        }

        if (FD_ISSET(sock_fd, &fdset)) {
            new_fd = accept(sock_fd, (struct sockaddr *)&client_addr, &sin_size);
            if (new_fd <= 0) {
                continue;
            }
            if (SetConnectParam(new_fd, &maxsock) < 0) {
                close(new_fd);
            }
        }
    }
    SocketRelease(sock_fd);
    return (void *)EXEC_SUCC;
}

// 业务逻辑处理函数，由worker线程池调度执行。
static void initServerExecuteCB(napi_env env, void *data)
{
    CommandStrData *commandStrData = (CommandStrData *)data;
    pthread_t tidSend;
    if (pthread_create(&tidSend, nullptr, SocketServiceCreate, nullptr) != EXEC_SUCC) {
        perror("pthread_create recv error");
        commandStrData->result.state = ERROR;
        commandStrData->result.result = "ERROR";
        commandStrData->result.time = "0s";
        exit(1);
    }
    pthread_detach(tidSend);
    commandStrData->result.state = SUCCESS;
    commandStrData->result.result = "SUCCESS";
    commandStrData->result.time = "1s";
}

static void initServerPromiseCompleteCB(napi_env env, napi_status status, void *data)
{
    CommandStrData *commandStrData = (CommandStrData *)data;

    napi_value result = nullptr;
    napi_value resState = nullptr;
    napi_value resResult = nullptr;
    napi_value resTime = nullptr;

    char tempChar[256] = {};
    napi_create_int32(env, (int32_t)commandStrData->result.state, &resState);
    strcpy_s(tempChar, sizeof(tempChar), commandStrData->result.result.c_str());
    napi_create_string_utf8(env, tempChar, strlen(tempChar), &resResult);

    bzero(tempChar, strlen(tempChar));
    strcpy_s(tempChar, sizeof(tempChar), commandStrData->result.time.c_str());
    napi_create_string_utf8(env, tempChar, strlen(tempChar), &resTime);

    napi_create_object(env, &result);
    napi_set_named_property(env, result, "state", resState);
    napi_set_named_property(env, result, "result", resResult);
    napi_set_named_property(env, result, "time", resTime);

    napi_resolve_deferred(env, commandStrData->deferred, result);

    // 删除napi_ref对象
    if (commandStrData->callback != nullptr) {
        napi_delete_reference(env, commandStrData->callback);
    }

    // 删除异步工作项
    napi_delete_async_work(env, commandStrData->asyncWork);
    delete commandStrData;
}

/**
 * @brief 控制机器狗做动作
 *
 * @param env 用于存储JS虚拟机的上下文
 * @param info 传递函数调用时的上下文信息，如this指针、参数列表值等
 * @return napi_value 返回处理结果
 */
static napi_value sendRobotDogCommand(napi_env env, napi_callback_info info)
{
    // 获取JS传入的命令（1个参数），值的类型是js类型（napi_value）
    size_t argc = 1;
    napi_value command[1];
    napi_value thisArg = nullptr;
    NAPI_CALL(env, napi_get_cb_info(env, info, &argc, command, &thisArg, nullptr));

    // 获取并判断js参数类型
    napi_valuetype valuetype0;
    NAPI_CALL(env, napi_typeof(env, command[0], &valuetype0));

    if (valuetype0 != napi_string) {
        napi_throw_type_error(env, nullptr, "Wrong arguments. string expected.");
        return nullptr;
    }

    // 创建promise
    napi_value promise = nullptr;
    napi_deferred deferred = nullptr;
    NAPI_CALL(env, napi_create_promise(env, &deferred, &promise));

    // 异步工作项上下文用户数据，传递到异步工作项的execute、complete之间传递数据
    auto commandStrData = new CommandStrData{
        .asyncWork = nullptr,
        .deferred = deferred,
    };

    // 将接收到的参数传入
    size_t getStrRes;
    NAPI_CALL(env, napi_get_value_string_utf8(env, command[0], commandStrData->robotDogCommand,
                                              (size_t)sizeof(commandStrData->robotDogCommand), &getStrRes));

    HILOG_INFO("=====socket=%{public}s,called,=======%{public}s", __func__, commandStrData->robotDogCommand);
    // 创建async work，创建成功后通过最后一个参数(commandStrData->asyncWork)返回async work的handle
    napi_value resourceName = nullptr;
    napi_create_string_utf8(env, "sendRobotDogCommand", NAPI_AUTO_LENGTH, &resourceName);
    napi_create_async_work(env, nullptr, resourceName, sendRobotDogCommandExecuteCB, 
    sendRobotDogCommandPromiseCompleteCB,(void *)commandStrData, &commandStrData->asyncWork);

    // 将刚创建的async work加到队列，由底层去调度执行
    napi_queue_async_work(env, commandStrData->asyncWork);

    // 返回promise
    return promise;
}

/**
 * @brief 接受设备信息
 *
 * @param env 用于存储JS虚拟机的上下文
 * @param info 传递函数调用时的上下文信息，如this指针、参数列表值等
 * @return napi_value 返回处理结果
 */
static napi_value recvMsg(napi_env env, napi_callback_info info)
{
    // 创建promise
    napi_value promise = nullptr;
    napi_deferred deferred = nullptr;
    NAPI_CALL(env, napi_create_promise(env, &deferred, &promise));

    // 异步工作项上下文用户数据，传递到异步工作项的execute、complete之间传递数据
    auto commandStrData = new CommandStrData {
        .asyncWork = nullptr,
        .deferred = deferred,
    };

    // 创建async work，创建成功后通过最后一个参数(commandStrData->asyncWork)返回async work的handle
    napi_value resourceName = nullptr;
    napi_create_string_utf8(env, "recvCompassMsg", NAPI_AUTO_LENGTH, &resourceName);
    napi_create_async_work(env, nullptr, resourceName, recvCompassMsgExecuteCB, recvCompassMsgPromiseCompleteCB,
                           (void *)commandStrData, &commandStrData->asyncWork);

    // 将刚创建的async work加到队列，由底层去调度执行
    napi_queue_async_work(env, commandStrData->asyncWork);

    // 返回promise
    return promise;
}

/**
 * @brief 初始化服务端
 *
 * @param env 用于存储JS虚拟机的上下文
 * @param info 传递函数调用时的上下文信息，如this指针、参数列表值等
 * @return napi_value 返回处理结果
 */
static napi_value initServer(napi_env env, napi_callback_info info)
{
    // 创建promise
    napi_value promise = nullptr;
    napi_deferred deferred = nullptr;
    NAPI_CALL(env, napi_create_promise(env, &deferred, &promise));

    // 异步工作项上下文用户数据，传递到异步工作项的execute、complete之间传递数据
    auto commandStrData = new CommandStrData{
        .asyncWork = nullptr,
        .deferred = deferred,
    };

    // 创建async work，创建成功后通过最后一个参数(commandStrData->asyncWork)返回async work的handle
    napi_value resourceName = nullptr;
    napi_create_string_utf8(env, "initServer", NAPI_AUTO_LENGTH, &resourceName);
    napi_create_async_work(env, nullptr, resourceName, initServerExecuteCB, initServerPromiseCompleteCB,
                           (void *)commandStrData, &commandStrData->asyncWork);

    // 将刚创建的async work加到队列，由底层去调度执行
    napi_queue_async_work(env, commandStrData->asyncWork);

    // 返回promise
    return promise;
}

// ----------------------------------------------------------------------------------
// napi_addon_register_func
static napi_value registerFunc(napi_env env, napi_value exports)
{
    static napi_property_descriptor desc[] = {
        DECLARE_NAPI_FUNCTION("initServer", initServer),
        DECLARE_NAPI_FUNCTION("recvMsg", recvMsg),
        DECLARE_NAPI_FUNCTION("sendRobotDogCommand", sendRobotDogCommand),

    };
    NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc));
    return exports;
}

/* 定义napi module
 * nm_modname: 模块名称，对应eTS使用为import nm_modname from '@ohos.ohos_shared_library_name'
 *             示例对应为：import hellonapi from '@ohos.hellonapi'
 * nm_register_func：API注册函数
 */
static napi_module tcpServerApi = {
    .nm_version = 1,
    .nm_flags = 0,
    .nm_filename = nullptr,
    .nm_register_func = registerFunc,
    .nm_modname = "tcpserverapi",
    .nm_priv = ((void *)0),
    .reserved = {0},
};

// ability module register
extern "C" __attribute__((constructor)) void tcpServerApiRegister() 
{ 
    napi_module_register(&tcpServerApi); 
}
